using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net.Mail;
using System.Diagnostics;
using System.Reflection;
using System.IO;
using System.Drawing;
using System.Globalization;
using System.Net.Mime;
using LeisureCenter.Forms;
using LeisureCenter.Properties;

namespace LeisureCenter.Core
{
    public class ExceptionInfo
    {
        public Exception exception;

        public ExceptionInfo(Object exception)
        {
            if (exception is Exception)
            {
                this.exception = (Exception)exception;
                this.message = GetExceptionText(this.exception);
            }
            else
            {
                this.message = GetGeneralInformations() + Environment.NewLine + exception.GetType().FullName + "\n" + exception.ToString();
            }
        }

        private string message;

        public string Message
        {
            get { return message; }
        }

        static string GetExceptionText(Exception ex)
        {
            string text = "";

            do
            {
                string currentText = "==================================================================" + Environment.NewLine;
                currentText += "exception class   : " + ex.GetType().FullName + Environment.NewLine;
                currentText += "exception message : " + ex.Message + Environment.NewLine;
                currentText += "target site       : " + ex.TargetSite + Environment.NewLine;
                currentText += "source            : " + ex.Source + Environment.NewLine;
                currentText += Environment.NewLine + GetEnhancedStackTrace(ex) + Environment.NewLine;

                text = currentText + text;
                ex = ex.InnerException;
            }
            while (ex != null);

            return GetGeneralInformations() + Environment.NewLine + text;
        }

        private static string GetGeneralInformations()
        {
            string infos = "";
            infos += @"date/time        : " + DateTime.Now.ToString(@"yyyy-MM-dd HH:mm:ss") + Environment.NewLine;
            infos += @"computer name    : " + Environment.MachineName + Environment.NewLine;
            infos += @"user name        : " + Environment.UserName + Environment.NewLine;
            infos += @"operating system : " + Environment.OSVersion + Environment.NewLine;
            infos += @"system language  : " + CultureInfo.CurrentCulture.EnglishName + Environment.NewLine;
            infos += @"processors       : " + Environment.ProcessorCount + Environment.NewLine;
            infos += @"version          : " + Assembly.GetExecutingAssembly().GetName().Version + Environment.NewLine;
            return infos;
        }

        private static string GetEnhancedStackTrace(Exception ex)
        {
            string result = "";
            StackTrace stackTrace = new StackTrace(ex, true);
            foreach (StackFrame frame in stackTrace.GetFrames())
            {
                result += GetStackFrameString(frame) + Environment.NewLine;
            }
            return result;
        }

        private static string GetStackFrameString(StackFrame sf)
        {
            string offset = "      ";
            string unit = "";
            string module = "      ";
            string line = "        ";

            // method
            MemberInfo info1 = sf.GetMethod();
            string method = info1.DeclaringType.Namespace + "." + info1.DeclaringType.Name + "." + info1.Name + "(";
            ParameterInfo[] infoArray1 = sf.GetMethod().GetParameters();
            int num1 = 0;
            foreach (ParameterInfo info2 in infoArray1)
            {
                num1++;
                if (num1 > 1)
                {
                    method += ", ";
                }
                method += info2.ParameterType.Name + " " + info2.Name;
            }
            method += ")";

            // module
            Assembly parentAssembly;
            if (Assembly.GetEntryAssembly() == null)
            {
                parentAssembly = Assembly.GetCallingAssembly();
            }
            else
            {
                parentAssembly = Assembly.GetEntryAssembly();
            }
            module = Path.GetFileName(parentAssembly.CodeBase);

            // unit and line number
            if ((sf.GetFileName() == null) || (sf.GetFileName().Length == 0))
            {
                offset = string.Format("N{0:#00000}", sf.GetNativeOffset());
            }
            else
            {
                unit = Path.GetFileName(sf.GetFileName());
                line = string.Format(@"{0:#0000}:{1:#000}", sf.GetFileLineNumber(), sf.GetFileColumnNumber());
                if (sf.GetILOffset() != -1)
                {
                    offset = string.Format("IL{0:#0000}", sf.GetILOffset());
                }
            }
            return string.Format(@"{0} {1} {2} {3} {4}", offset, module, unit, line, method);
        }
    }

    public class ExceptionHandler
    {
        private static Queue<ExceptionInfo> exceptions = new Queue<ExceptionInfo>();
        private delegate void ExceptionDelegate(Exception x);

        public ExceptionHandler()
        {
            if (!Debugger.IsAttached)
            {
                Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
                Application.Idle += new EventHandler(Application_Idle);
            }
        }

        void Application_Idle(object sender, EventArgs e)
        {
            lock (((ICollection)exceptions).SyncRoot)
            {
                while (exceptions.Count > 0)
                    HandleException(exceptions.Dequeue());
            }
        }

        private static void HandleException(ExceptionInfo exceptionInfo)
        {
            Program.LeisureCenterInstance.MainForm.DisableScreensaver();
            try
            {
                Form background = Program.LeisureCenterInstance.UIManager.CreateAlphaBackgroundForm(Color.Red);
                using (background)
                {
                    background.Show();
                    try
                    {
                        ExceptionHandlerForm form = new ExceptionHandlerForm(exceptionInfo);
                        using (form)
                        {
                            switch (form.ShowDialog())
                            {
                                case DialogResult.Abort:
                                    Application.Exit();
                                    break;
                                case DialogResult.Retry:
                                    Application.Restart();
                                    break;
                            }
                        }
                    }
                    finally
                    {
                        background.Hide();
                    }
                }
            }
            finally
            {
                Program.LeisureCenterInstance.MainForm.EnableScreensaver();
            }
        }

        private static void HandleException(Object exception)
        {
            ExceptionInfo exceptionInfo = new ExceptionInfo(exception);

            HandleException(exceptionInfo);
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            PublishOnMainThread(e.ExceptionObject);
        }

        public static void PublishOnMainThread(Object exception)
        {
            if (Program.LeisureCenterInstance.MainForm.InvokeRequired)
            {
                // Invoke executes a delegate on the thread that owns _MainForms's underlying window handle.
                Program.LeisureCenterInstance.MainForm.Invoke(new ExceptionDelegate(HandleException), new object[] { exception });
            }
            else
            {
                HandleException(exception);
            }
        }

        private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            HandleException(e.Exception);
        }

        public static void SendMailForException(ExceptionInfo exceptionInfo)
        {
            try
            {
                LeisureCenter.Properties.Settings settings = new LeisureCenter.Properties.Settings();
                SmtpClient smtpClient = new SmtpClient(settings.SmtpHost);
                MailMessage message = new MailMessage();
                message.To.Add(new MailAddress(@"bugs@cfagn.net"));
                Version version = Assembly.GetExecutingAssembly().GetName().Version;
                string mail = String.Format(@"leisurecenter_{0}@cfagn.net", version.ToString());
                message.From = new MailAddress(mail, @"LeisureCenter");
                message.Subject = String.Format(@"[LeisureCenter][{0}] bug report", version.ToString());
                Attachment attachment = Attachment.CreateAttachmentFromString(exceptionInfo.Message, "bugreport.txt");
                message.Attachments.Add(attachment);
                //message.Body = exceptionInfo.Message;
                smtpClient.Send(message);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
}
